/**
	This file is part of Fighting Field.

	Fighting Field is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Fighting Field is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fighting Field.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Game.h"

#include "GuiManager.h"
#include "InputManager.h"

template<> FF::Game* Ogre::Singleton<FF::Game>::ms_Singleton = NULL;

namespace FF
{
	const Ogre::Real Game::msStepDt(0.15f); // in s

	Game::Game()
		: mRoot(NULL)
		, mWindow(NULL)
		, mInputManager(NULL)
		, mGuiManager(NULL)
		, mPrevStep(0.f)
	{
		mRoot = new Ogre::Root();

		// init render system
		Ogre::RenderSystemList rsList = mRoot->getAvailableRenderers();
		if (!rsList.size())
		{
			OGRE_EXCEPT(Ogre::Exception::ERR_INVALID_STATE, "Cannot find render system.", "Game::Game");
		}
		mRoot->setRenderSystem(rsList.front());

		// create window and render window
		mRoot->initialise(false, Ogre::StringUtil::BLANK, Ogre::StringUtil::BLANK);

		// TODO properties
		unsigned int sizeX = 800;
		unsigned int sizeY = 600;
		bool fullscreen = false; 
		Ogre::NameValuePairList lParams;
		lParams["FSAA"] = "0"; 
		lParams["vsync"] = "true";
		CreateRenderWindow(sizeX, sizeY, fullscreen, &lParams);

		SetupView();

		// register game
		mRoot->addFrameListener(this);

		mInputManager = new InputManager(mWindow);
		mWindow->addListener(mInputManager);
		
		InitResources();

		Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("Essential");
            
		mGuiManager = new GuiManager("GuiRoot", mWindow, mInputManager->GetMouse());
		mInputManager->RegisterListener(InputLayer::Gui, mGuiManager);
	}

	Game::~Game()
	{
		DELETE(mGuiManager);

		ReleaseResources();

		mWindow->removeListener(mInputManager);
		DELETE(mInputManager);

		mRoot->removeFrameListener(this);
		mWindow = NULL;
		
		DELETE(mRoot);
	}

	void Game::Go()
	{
		mRoot->startRendering();
	}

	void Game::CreateRenderWindow(unsigned int width, unsigned int height, bool fullScreen, const Ogre::NameValuePairList* miscParams)
	{
		// destroy old window
		if (mWindow)
		{
			mRoot->detachRenderTarget(mWindow);
			mRoot->destroyRenderTarget(mWindow);
		}
		mWindow = mRoot->createRenderWindow(FF_TITLE, width, height, fullScreen, miscParams);
	}

	Game& Game::Get()
    {
        assert(ms_Singleton);
		return (*ms_Singleton);
    }

	Game* Game::GetPtr()
    {
        return ms_Singleton;
    }

    
	bool Game::frameStarted(const Ogre::FrameEvent& evt)
	{
		mInputManager->Update(evt.timeSinceLastFrame);
		return true;
	}
		
	bool Game::frameRenderingQueued(const Ogre::FrameEvent& evt)
	{
		// step
		mPrevStep += evt.timeSinceLastFrame;
		while (mPrevStep >= msStepDt)
		{
			mPrevStep -= msStepDt;
			StateMachine::Step(msStepDt);
		}

		// update
		mGuiManager->Update(evt.timeSinceLastFrame);
		StateMachine::Update(evt.timeSinceLastFrame);

		return true;
	}
		
	bool Game::frameEnded(const Ogre::FrameEvent& evt)
	{
		// quit if window was closed
		if (mWindow->isClosed())
		{
			return false;
		}
		return true;
	}

	void Game::InitResources()
	{
		// load resource paths from config file
		Ogre::ConfigFile cf;
		cf.load("resources.cfg");

		Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
		Ogre::String sec, type, arch;

		// go through all specified resource groups
		while (seci.hasMoreElements())
		{
			sec = seci.peekNextKey();
			Ogre::ConfigFile::SettingsMultiMap* settings = seci.getNext();
			Ogre::ConfigFile::SettingsMultiMap::iterator i;

			// go through all resource paths
			for (i = settings->begin(); i != settings->end(); i++)
			{
				type = i->first;
				arch = i->second;

				Ogre::ResourceGroupManager::getSingleton().addResourceLocation(arch, type, sec);
			}
		}
	}

	void Game::ReleaseResources()
	{
		Ogre::ResourceGroupManager& grpMgr = Ogre::ResourceGroupManager::getSingleton();
		Ogre::StringVector& groups = grpMgr.getResourceGroups();
		Ogre::StringVector::iterator itr, end = groups.end();
		for (itr = groups.begin(); itr != end; itr++)
		{
			grpMgr.destroyResourceGroup(*itr);
		}
	}

	void Game::SetupView()
	{
		// setup default viewport layout and camera
		mWindow->removeAllViewports();
		Ogre::SceneManager* sm = mRoot->createSceneManager(Ogre::ST_GENERIC, "DummyScene");
		Ogre::Camera* cam = sm->createCamera("DummyCamera");
		mWindow->addViewport(cam);
	}
}
